/*
This file is part of MMM.

MMM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

MMM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with MMM.  If not, see <http://www.gnu.org/licenses/>.
*
* @package    MMM
* @author     Andre Meixner
* @copyright  2017 High Performance Humanoid Technologies (H2T), Karlsruhe, Germany
*
*/

#ifndef __MMM_MULTIPLEFACTORYPLUGINLOADER_H_
#define __MMM_MULTIPLEFACTORYPLUGINLOADER_H_

#include "FactoryPluginLoader.h"

namespace MMM {

template <typename Factory>
/*! \brief A templated class for easy plugin loading from libraries having same ids but different locations. */
class MMM_IMPORT_EXPORT MultipleFactoryPluginLoader : public FactoryPluginLoader<Factory>
{
public:

    MultipleFactoryPluginLoader(const std::vector<std::string> &libPaths) : FactoryPluginLoader<Factory>(libPaths) {
    }

    MultipleFactoryPluginLoader(const std::string &librarySearchPath) : FactoryPluginLoader<Factory>(librarySearchPath) {
    }

    MultipleFactoryPluginLoader() : FactoryPluginLoader<Factory>() {
    }

    std::map<std::string, std::map<std::string, boost::shared_ptr<Factory> > > getAllFactories() {
        return allFactories;
    }

    std::map<std::string, boost::shared_ptr<Factory> > getAllFactories(const std::string &name) {
        return allFactories[name];
    }

    bool setActivePlugin(const std::string &type, const std::string &path, bool active = true) {
        if (active) {
            boost::shared_ptr<Factory> factory = this->factories[type];
            if (!factory || (factory && factory->getPath() != path)) {
                boost::shared_ptr<Factory> activeFactory = allFactories[type][path];
                if (activeFactory) {
                    this->factories[type] = activeFactory;
                    return true;
                }
                else MMM_ERROR << "No factory with type " << type << " and path " << path << " found! (SHOULD NOT OCCUR!)" << std::endl;
            }
            return false;
        } else {
            auto it = this->factories.find(type);
            if (it->second->getPath() != path) return false;
            this->factories.erase(it);
            return true;
        }
    }

protected:
    int addFactory(boost::shared_ptr<Factory> factory) {
        if (this->factories.find(factory->getName()) == this->factories.end()) {
            this->factories[factory->getName()] = factory;
        }
        if (allFactories[factory->getName()].find(factory->getPath()) == allFactories[factory->getName()].end()) {
            allFactories[factory->getName()][factory->getPath()] = factory;
            return 1;
        }
        MMM_INFO << "Duplicate plugin path " << factory->getPath() << " found." << std::endl;
        return 0;
    }

private:
    std::map<std::string, std::map<std::string, boost::shared_ptr<Factory> > > allFactories;

};

}
#endif // __MMM_MULTIPLEFACTORYPLUGINLOADER_
